package com.smart.common.aop;


import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alicp.jetcache.Cache;
import com.smart.common.annotation.AccessLimit;
import com.smart.common.cache.CacheService;
import com.smart.common.entities.ajax.AjaxResult;
import com.smart.utils.ResponseUtil;
import lombok.extern.log4j.Log4j2;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 访问限制拦截器
 * <p>
 * API防刷拦截器实现
 * </p>
 *
 * @author Administrator
 * @date 2022/10/23
 */


@Log4j2
public class AccessLimitInterceptor implements HandlerInterceptor {

    private final CacheService cacheService;

    public AccessLimitInterceptor(CacheService cacheService) {
        this.cacheService = cacheService;
    }

    /**
     * 请求开始之前进行数据拦截
     */
    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {

        HandlerMethod handlerMethod;
        Method method;
        //org.springframework.web.method.HandlerMethod 包不要到导错了
        if (handler instanceof HandlerMethod) {
            handlerMethod = (HandlerMethod) handler;
            method = handlerMethod.getMethod();
            //获取方法处理器
            AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
            //标注了注解的方法才进行拦截数据判断
            if (accessLimit != null) {
                String clientIp = ServletUtil.getClientIP(request);
                long seconds = accessLimit.seconds();
                //最大请求书
                int maxCount = accessLimit.maxCount();
                //访问的方法路径
                String methodName = handlerMethod.getShortLogMessage();
                //访问者IP
                String visitorIp = "0:0:0:0:0:0:0:1".equals(clientIp) ? "127.0.0.1" : clientIp;
                String redisKey = DigestUtil.md5Hex16(visitorIp.concat("::").concat(methodName));
                Cache<String, Integer> visitorCache = cacheService.getVisitorCache();
                Integer count = visitorCache.get(redisKey);
                if (count == null) {
                    visitorCache.put(redisKey, 1, seconds, TimeUnit.SECONDS);
                } else if (count < maxCount) {
                    visitorCache.put(redisKey, 1 + count);
                } else {
                    AjaxResult<Object> fail = AjaxResult.fail(3000, "请求过于频繁,请稍后再试");
                    ResponseUtil.out(response, fail);
                    return false;
                }
            }
        }
        return true;
    }

}